using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.Base.Entity;
using static T.FAS.Runtime.Metadata.Entity.DataModel.CompositeDataObjectRefSchemaEntity;

namespace T.FAS.Runtime.DataAccess.Engine
{
    /// <summary>
    /// 领域对象Insert SQL构造策略
    /// </summary>
    class InsertSqlBuildStrategy : BaseSqlBuildStrategy
    {
        /// <summary>
        /// 构造不包含数据的Sql（即SqlSchema）。
        /// </summary>
        /// <param name="context">Sql构造的上下文信息。</param>
        public override SqlStatementCollection BuildTableSqlSchema(SqlBuildingContext context)
        {
            if (context == null)
                throw new FasException(DataAccessErrorCode.Create.NullValue, CommonStringClassEngine.SqlBuildStrategy_ContextCannotBeNull);

            var sqls = new SqlStatementCollection();

            var insertStatement = SQLStatementFactory.CreateSqlStatement(SqlStatementType.Insert, context.DbType) as InsertSqlStatement;
            base.HandlingSqlStatement(insertStatement, context);
            HandlingInsertFields(insertStatement, context);
            insertStatement.SqlBuildingInfo.DataSource = context.DataSource;

            sqls.Add(insertStatement);

            return sqls;
        }

        /// <summary>
        /// 在SqlSchema基础上，构造包含数据的Sql。
        /// </summary>
        /// <param name="sqls">Sql语句对象集合。</param>
        /// <param name="context">Sql构造的上下文信息。</param>
        public override void BuildTableSqlDetail(SqlStatementCollection sqls, SqlBuildingContext context)
        {
            var sql = sqls.FirstOrDefault(x => x.SqlBuildingInfo.TableName == context.TableName);
            if (sql == null) return;

            var insertStatement = sql as InsertSqlStatement;
            //insertStatement.SqlBuildingInfo = InitSqlBuildingInfo(context.CommonObject, context.Node, context.DataObject, context.TableName);

            this.HandlingFieldsAddData(insertStatement, context);
        }

        /// <summary>
        /// 构造Insert语句中的插入字段
        /// </summary>
        /// <param name="sql">Insert语句</param>
        /// <param name="context">SQL构造上下文</param>
        protected void HandlingInsertFields(InsertSqlStatement sql, SqlBuildingContext context)
        {
            foreach (DataColumn col in context.DataObject.Columns)
            {
                //统一数据访问服务掩码改造：没有权限的列跳过
                if (context.ControlContext != null && !context.ControlContext.IsHasPermission(context.DataObject, col))
                {
                    continue;
                }
                sql.InsertFields.ChildCollection.Add(new InsertField()
                {
                    FieldName = col.ColumnName,
                    IsUseVarBinding = true
                });
                // 数据国际化改造：CDO.Create支持多语言列插入
                if (col.IsInternational)    //处理多语言字段
                {
                    if (context.ControlContext != null)
                    {
                        var mapping = InternationalTools.GetLanguageMapping(
                                            context.ControlContext.InternationalMode,
                                            context.ControlContext.SpecifiedLanguages);
                        if (mapping.Count > 0)
                        {
                            foreach (var langKey in mapping.Keys)
                            {
                                string fieldName = InternationalTools.GetRealFieldName(context.DataObject, col.ColumnName, langKey);
                                sql.InsertFields.ChildCollection.Add(new InsertField()
                                {
                                    FieldName = fieldName,  //存储Name_lang1
                                    IsUseVarBinding = true,
                                    IsInternational = true, //只有辅助语言列会打标记，主语言列走原有逻辑
                                    LangCode = langKey,
                                    I18NCode = mapping[langKey],
                                    OriginalFieldName = col.ColumnName,
                                });
                            }
                        }
                    }
                }
                //统一数据访问服务掩码改造
                if (MaskTool.EnableFASMaskStorage(col.MaskingRule))    //处理掩码字段
                {
                    string maskFieldName = DataColumn.GetMaskColumnName(context.DataObject, col.ColumnName);
                    sql.InsertFields.ChildCollection.Add(new InsertField()
                    {
                        FieldName = maskFieldName,  //存储Name_mask
                        IsMask = true,              //只有掩码列会打标记，主语言列走原有逻辑
                        IsUseVarBinding = true,
                        OriginalFieldName = col.ColumnName,
                    });
                }
            }
            var fieldList = string.Join(",", context.DataObject.Columns.Select(o => o.ColumnName));
            //LocalLogService.Write($"构建插入语句FieldList：CompositeNodeObject:{context?.Node?.ID}, DataObject:{context?.DataObject?.ID}, FieldList:{fieldList}");
        }

        /// <summary>
        /// 处理带数据的元素
        /// </summary>
        /// <param name="sql">Insert语句</param>
        /// <param name="context">策略的上下文</param>
        protected void HandlingFieldsAddData(InsertSqlStatement sql, SqlBuildingContext context)
        {
            //待排除的列
            List<InsertField> excludeField = new List<InsertField>();

            //LocalLogService.Write($"HandlingFieldsAddData:=>DMO:{context.Node.ID} DO:{context.DataObject.ID};sql_insert_fields count:{sql.InsertFields.ChildCollection.Count}; context_fields_count:{context.DataContext.GetCurrentDataContextItem(context.Node.ID).Data.Keys.Count}");
            //LocalLogService.Write(context.DataContext);

            var dbTypeStr = context.CommonObject.RootCompositeNodeObject.DataObject.Database.DbType;
            DatabaseType dbType = (DatabaseType)Enum.Parse(typeof(DatabaseType), dbTypeStr);
            //获取CDO节点的数据Item
            DataContextItem dataContextItem = context.DataContext.GetCurrentDataContextItem(context.Node.ID);

            foreach (InsertField insertField in sql.InsertFields.ChildCollection)
            {
                //判断插入的数据对象列的有效性
                DataColumn insertCol = null;
                if (insertField.IsInternational || insertField.IsMask)
                {
                    insertCol = context.DataObject.Columns.FirstOrDefault(i => i.ColumnName == insertField.OriginalFieldName);
                }
                else
                {
                    insertCol = context.DataObject.Columns.FirstOrDefault(i => i.ColumnName == insertField.FieldName);
                } 

                if (insertCol == null)
                {
                    throw new FasException(DataAccessErrorCode.CompositeDataObject.ColumnError, CommonStringClassEngine.InsertSqlBuildStrategy_HandlingFieldsAddData + insertField.FieldName);
                }
                CompositeNodeObjectElement insertElement = context.Node.Elements.FirstOrDefault(i => i.DataColumnID == insertCol.ID);
                //兼容当DataObject.Column存在，但在CDO对应节点中不存在该列映射的Element的情况
                if (insertElement == null)
                {
                    if (insertCol.IsNullable == false)
                    {
                        throw new FasException(DataAccessErrorCode.CompositeDataObject.ElementError, CommonStringClassEngine.InsertSqlBuildStrategy_HandlingFieldsAddData1 + insertCol.ID);
                    }
                    else
                    {
                        excludeField.Add(insertField);
                    }

                    continue;
                }
                else
                {
                    //特殊处理系统字段，系统字段不走从DataConextItem中取值的逻辑。
                    if (insertCol.IsSystemField())
                    {
                        InsertValue systemFieldValue = new InsertValue();
                        systemFieldValue.DataType = Convert.ToInt32(insertElement.DataType);
                        systemFieldValue.DbColumnDataType = insertCol.GetDatabaseColumnType();
                        systemFieldValue.Value = GetValue(insertField, insertCol, dbType, null);
                        sql.InsertValues.ChildCollection.Add(systemFieldValue);
                        continue;
                    }
                    if (insertField.IsInternational)
                    {
                        //string langElementID = InternationalTools.GetRealFieldName(context.DataObject, insertElement.ID, insertField.LangCode);
                        string langElementID = InternationalTools.GetDataItemElementIDKey(context.DataObject, insertElement, insertField.LangCode);
                        if (dataContextItem.Data.ContainsKey(langElementID) == false)
                        {
                            excludeField.Add(insertField);
                            continue;
                        }
                    }
                    else if (insertField.IsMask)
                    {
                        string maskElementID = MaskTool.GetMaskDataItemElementIDKey(context.DataObject, insertElement);
                        if (dataContextItem.Data.ContainsKey(maskElementID) == false)
                        {
                            excludeField.Add(insertField);
                            continue;
                        }
                    }
                    else
                    {
                        if (dataContextItem.Data.ContainsKey(insertElement.ID) == false)
                        {
                            excludeField.Add(insertField);
                            continue;
                        }
                    }
                }

                InsertValue fieldValue = new InsertValue();
                fieldValue.DataType = Convert.ToInt32(insertElement.DataType);
                fieldValue.DbColumnDataType = insertCol.GetDatabaseColumnType();
                object value = null;
                if (insertField.IsInternational)
                {
                    //string langElementID = InternationalTools.GetRealFieldName(context.DataObject, insertElement.ID, insertField.LangCode);
                    string langElementID = InternationalTools.GetDataItemElementIDKey(context.DataObject, insertElement, insertField.LangCode);
                    value = dataContextItem.Data[langElementID];
                }
                else if (insertField.IsMask)
                {
                    string maskElementID = MaskTool.GetMaskDataItemElementIDKey(context.DataObject, insertElement);
                    value = dataContextItem.Data[maskElementID];
                    fieldValue.DataType = Convert.ToInt32(DataType.String); //掩码字段只能为String类型
                }
                else
                {
                    value = dataContextItem.Data[insertElement.ID];
                }
                fieldValue.Value = ElementValueWrapper.ConvertElementValue(insertElement, value);
                fieldValue.Value = GetValue(insertField, insertCol, dbType, fieldValue.Value);

                sql.InsertValues.ChildCollection.Add(fieldValue);

            }

            //从插入列中排除
            if (excludeField != null && excludeField.Count > 0)
            {
                foreach (var field in excludeField)
                {
                    sql.InsertFields.ChildCollection.Remove(field);
                }
            }

            if (sql.InsertFields.ChildCollection.Count == 0)
                throw new FasException(DataAccessErrorCode.Create.InsertFieldError, string.Format(CommonStringClassEngine.InsertSqlBuildStrategy_HandlingFieldsAddData2,
                    context.CommonObject.ID, context.Node.ID));
            if (sql.InsertFields.ChildCollection.Count != sql.InsertValues.ChildCollection.Count)
                throw new FasException(DataAccessErrorCode.Create.InsertFieldError, string.Format(CommonStringClassEngine.InsertSqlBuildStrategy_HandlingFieldsAddData3,
                    context.CommonObject.ID, context.Node.ID));
        }

        /// <summary>
        /// 新增时处理低代码系统字段赋值
        /// </summary>
        /// <param name="insertField"></param>
        /// <param name="col"></param>
        /// <param name="dbType"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private object GetValue(InsertField insertField, DataColumn col, DatabaseType dbType, object value)
        {
            if (col.IsTimestampField())
            {
                //暂时用应用服务器时间
                insertField.IsUseVarBinding = true;
                return DateTime.Now; //时间保留毫秒精度

                ////应用数据库时间
                //insertField.IsUseVarBinding = false;    //时间使用数据库函数
                //return DatabaseTool.GetDefaultDateFunc(dbType);
            }
            else
            {
                switch (col.SysColumnFlag)
                {
                    case SysColumnFlag.Creator:
                    case SysColumnFlag.LastModifier:
                        return UserUtil.GetCurrentUserID();
                    case SysColumnFlag.CreateTime:
                        insertField.IsUseVarBinding = true;
                        DateTime dateTime = DACContextHelper.GetTimestamp(); //线程上下文中获取  
                        return dateTime;
                    case SysColumnFlag.LastModifierName:
                    case SysColumnFlag.CreatorName:
                        return UserUtil.GetCurrentUserName();
                    case SysColumnFlag.None:
                    default:
                        //兼容旧代码，SysColumnFlag字段映射的配置优先级最高，应对Creator列SystemFlag=CreatorName。内容存储姓名（CreatorName）的场景
                        if (col.ColumnName == DataModelSystemConst.Creator
                            || col.ColumnName == DataModelSystemConst.LastModifier)
                            return UserUtil.GetCurrentUserID();
                        else if (col.ColumnName == DataModelSystemConst.CreatorName
                            || col.ColumnName == DataModelSystemConst.LastModifierName)
                            return UserUtil.GetCurrentUserName();
                        else if (col.ColumnName == DataModelSystemConst.CreateTime
                           || col.ColumnName == DataModelSystemConst.LastModifyTime)
                        {
                            //insertField.IsUseVarBinding = false;    //时间使用数据库函数
                            //return DatabaseTool.GetDefaultDateFunc(dbType);
                            insertField.IsUseVarBinding = true;
                            dateTime = DACContextHelper.GetTimestamp(); //线程上下文中获取  
                            return dateTime;
                        }
                        break;
                }
            }
            return value;
        }

        ///// <summary>
        ///// 处理通用中间对象的节点的GSPAbsDBTable。
        ///// </summary>
        ///// <param name="sql">Sql语句对象。</param>
        ///// <param name="CompositeDataObject">领域模型。</param>
        ///// <param name="CompositeNodeObject">领域对象。</param>
        ///// <param name="dataObject">领域对象对应的数据对象。</param>        
        ///// <param name="tableName">表名称。</param>
        //protected virtual void HandlingSqlStatement(SqlStatement sql, CompositeDataObject CompositeDataObject, CompositeNodeObject CompositeNodeObject, DataObject dataObject, string tableName, string dataSource)
        //{
        //    sql.NodeID = CompositeNodeObject.ID;
        //    sql.CommonObjectID = CompositeDataObject.ID;
        //    sql.NodeVersion = CompositeDataObject.Version.ToString();
        //    sql.CommonObjectVersion = CompositeDataObject.Version.ToString();
        //    sql.TableName = tableName;

        //    //构造SqlBuildingInfo  
        //    sql.SqlBuildingInfo = this.InitSqlBuildingInfo(CompositeDataObject, CompositeNodeObject, dataObject, tableName, dataSource);

        //    //复合主键                       
        //    foreach (var item in dataObject.PKColumns)
        //    {
        //        var keyField = new SqlPrimaryKeyField();
        //        keyField.FieldName = item.ColumnName;
        //        sql.PrimaryKeys.ChildCollection.Add(keyField);
        //    }
        //}
    }
}